Skip to content

Upgrade Gentl backend#44

Merged
C-Achard merged 34 commits intocy/pre-release-fixes-2.0from
cy/upgrade-gentl-backend
Feb 16, 2026
Merged

Upgrade Gentl backend#44
C-Achard merged 34 commits intocy/pre-release-fixes-2.0from
cy/upgrade-gentl-backend

Conversation

@C-Achard
Copy link

@C-Achard C-Achard commented Feb 12, 2026

Upgraded Gentl backend

  • Upgrades the Gentl backend to support all UI features
  • Improves testing and coverage for the backend

Partial changelog (tests)

This pull request primarily refactors and expands the GentL/harvesters backend test infrastructure to provide stricter, more flexible, and more realistic test doubles. The changes make it easier to simulate various device enumeration and lifecycle scenarios, improve test clarity, and facilitate more robust backend testing. It also includes minor documentation and configuration updates.

Test infrastructure improvements for GentL/harvesters backend:

  • Major refactor of tests/cameras/backends/conftest.py to replace the _DeviceInfoAdapter with a more flexible _info_get function, update and document all fake classes, and add call tracing for lifecycle methods, making test doubles stricter and more realistic. [1] [2]
  • Addition of new fixtures and factories (gentl_inventory, fake_harvester_factory, gentl_settings_factory) to allow tests to easily customize device enumeration and camera settings for the GentL backend.
  • Improved the patch_gentl_sdk fixture to patch the backend more robustly, including bypassing filesystem/SDK dependencies and ensuring the backend uses the new, stricter test doubles.

Minor documentation and configuration changes:

  • Updated comments and docstrings throughout the test doubles and fixtures for clarity and future maintainability. [1] [2]
  • Simplified .coveragerc by removing explicit omits, likely to ensure all backend code is included in coverage reports.
  • Added a FIXME comment in dlclivegui/gui/main_window.py to mark deprecated behavior for future removal.

Add device enumeration and rebinding utilities to the Aravis backend: implement quick_ping, discover_devices, rebind_settings, _arv_snapshot_devices and _safe_str to allow probing and best-effort rebinds without opening cameras. Update open() to record and refresh device identity (device_id, physical id, vendor/model/serial, label) into CameraSettings properties and compute a higher-quality label from the opened camera. Change namespace key usage from camera_id to device_id and import CameraSettings and DetectedCamera to support the new APIs.
Introduce comprehensive test scaffolding for camera backends: add fake SDK implementations and patching fixtures in tests/cameras/backends/conftest.py (FakeAravis, FakePylon, FakeHarvester and helpers to monkeypatch aravis/pypylon/harvesters). Add backend-agnostic contract tests in tests/cameras/backends/test_generic_contracts.py to validate backend capability shapes, availability reporting, safe discovery, create/close semantics, optional accelerator warnings, and a GUI identity helper. These changes enable running backend contract tests in CI without real SDKs and help ensure consistent backend behavior.
Make test backend mocks more robust for SDK-less unit tests: add numpy import and a stricter Aravis availability check (require gi and Aravis typelib), expose HARVESTERS_AVAILABLE, and refine force_pypylon_unavailable to set pylon=None. Replace the simple FakeHarvester with a richer fake GenTL implementation (device info adapter, node/node_map, image acquirer, payload/components, timeout exception) that supports create()/create_image_acquirer(), start/stop/fetch and realistic buffer payloads. Patch GenTLCameraBackend to avoid CTI file searching during tests. Also remove pytest.mark.integration markers from many aravis tests so they run as unit tests.
Add robust GenTL backend features: read backend options from properties["gentl"], support CTI search paths, and a fast_start probe mode that avoids starting acquisition. Introduce stable device identity handling (device_id with serial: and fp: formats) while keeping legacy serial/index fallbacks. Improve device selection logic (match device_id, exact/substring serial matching, index fallback) and persist discovered metadata back into settings. Add helpers: _device_id_from_info, discover_devices, rebind_settings, and quick_ping for reliable discovery/rebinding and UI integration. Make Harvester usage more defensive and handle different device_info shapes.
Introduce a lightweight, SDK-free fake GenTL/Harvesters implementation and associated pytest fixtures to enable strict lifecycle unit tests for the gentl backend. Replaces the old _DeviceInfoAdapter with a robust attribute/dict reader (_info_get), implements _FakeNode/_FakeNodeMap/_FakeComponent/_FakePayload and a dataclass FakeImageAcquirer with strict start/fetch semantics and call tracing. Adds FakeHarvester with inventory-driven device_info_list, create/create_image_acquirer compatibility and call recording, plus fixtures gentl_inventory, fake_harvester_factory, patch_gentl_sdk (patches backend to use fakes) and gentl_settings_factory. Also adds tests/cameras/backends/test_gentl_backend.py with comprehensive unit tests covering open/read/close, fast_start behavior, device selection (serial/fingerprint), rebind_settings, discover_devices, resolution alignment, pixel format handling, and other edge cases to validate the gentl backend behavior.
@C-Achard C-Achard self-assigned this Feb 12, 2026
@C-Achard C-Achard added enhancement New feature or request camera Related to cameras and camera backends labels Feb 12, 2026
C-Achard and others added 12 commits February 12, 2026 14:37
Refactor and harden BaslerCameraBackend: add a basler namespace view/ensure helpers, stable device_id (serial) handling with legacy fallbacks, and persistence of device identity. Introduce fast_start probe mode to avoid starting acquisition/creating converter, and an apply_transforms option to perform rotation/crop in read().

Add testable device enumeration helpers ( _enumerate_devices_cls, discover_devices, get_device_count, quick_ping ) and utility operations (rebind_settings, sanitize_for_probe) to improve discovery, rebinding, and fast probing. Improve open()/read()/close() with safer node access, better logging, explicit handling of exposure/gain/fps, resolution snapping to node increments/ranges, read-back of actual telemetry, and clearer errors when operating in fast-start probe mode. Maintain backward compatibility with legacy properties (serial, resolution) while preferring namespaced options.
Move camera probe options out of commented code and explicitly disable fast_start and apply_transforms during preview probing to avoid double transforms (Basler) and ensure a full open when probing. Replace _needs_preview_reopen with _should_restart_preview and implement a backend-agnostic policy: restart preview only for camera-side capture parameter changes (width, height, fps, exposure, gain) and do not restart for rotation/crop to provide a faster UX. Improve _apply_camera_settings by preventing applies while a loader is active, computing/logging a pre-apply diff, persisting the validated model, and then deciding whether to restart the preview; use a short QTimer delay for driver stability when restarting. Add debug/info logging around preview start/stop, loader success, backend close, and more robust error logging for loader failures. Small cleanup of exception handling and safer restart decision behavior.
Remove the apply_transforms flag and associated transform logic from dlclivegui/cameras/backends/basler_backend.py. This deletes the _apply_crop helper, the attribute initialization for _apply_transforms, and the code that applied rotation and cropping to captured frames, leaving frames unmodified by the backend. Simplifies the Basler backend by delegating any image transforms to upstream/post-processing.
Config: tighten CameraSettings crop handling — treat all-zeros as "no crop", allow x1/y1 == 0 to mean "to edge", and only enforce x1>x0 / y1>y0 when x1/y1 are explicitly >0.

GUI (camera_config_dialog): remove backend apply_transforms tweak in probe worker; add robust auto-commit behavior to prevent losing/accepting invalid edits when switching selection, adding/removing/reordering cameras or closing the dialog. Introduce _commit_pending_edits() which attempts to apply pending changes (and shows a warning on failure), make _apply_camera_settings() return success as a boolean, and block UI actions when validation fails. Also auto-applies pending settings on OK and ensures previews are stopped appropriately.

Main window: ensure camera.properties is a dict and set backend-namespaced properties; set fast_start in backend namespace (and initialize properties safely) instead of the old top-level quick default.

These changes improve UX by validating/committing edits proactively and make crop semantics clearer and more flexible.
Add a visual "dirty" state for the Apply Settings button to make unapplied camera config edits more visible. Introduces _set_apply_dirty to toggle button text, icon and tooltip, and a _mark_dirty handler that is connected to various input signals (valueChanged, currentIndexChanged, stateChanged). Ensure the dirty/state is cleared when populating settings and after a successful apply, and unify/replace previous ad-hoc enable calls so the button reliably reflects pending edits.
Basler backend: simplify and harden exposure/gain configuration by only applying positive values, turning off auto modes when available, and logging failures as warnings. Harden stream lifecycle for previews by stopping grabbing if needed, creating the ImageFormatConverter before StartGrabbing, forcing MaxNumBuffer, starting grabbing reliably and logging grab state; also wrap StopGrabbing on shutdown to ignore errors.

Camera config UI: refine property merge and UI behavior — avoid reloading the form after merge, safely refresh camera labels by guarding null lists and blocking signals, ignore redundant selection events, and add explicit preview restart/start helpers (_restart_preview_for_camera and _start_preview_with_camera). Ensure previews never use fast_start mode and improve logging around selection and preview startup.
Avoid unwanted UI-driven side-effects when programmatically updating or selecting cameras. Add suppression flags to skip selection handlers/ actions during updates and preview startup, and wrap preview start/stop in a try/finally to restore suppression. Block widget signals while loading camera settings to prevent spurious change events, add exposure/gain to the form field groups, and skip probing when a preview is active. Also add a few diagnostic logs to help trace selection/preview behavior.
Improve camera dialog test fixture to show the dialog, yield it to tests, and perform a robust teardown: stop preview, reject/close the dialog, and wait for loader/scan worker threads and preview state to clear. Also add an autouse fixture that monkeypatches QMessageBox methods to raise on any unexpected modal dialog, preventing tests from hanging due to blocking message boxes. Small formatting/inline cleanup of MultiCameraSettings initialization included.
Ensure the probe worker is cleanly stopped when shutting down: call request_cancel(), wait (up to 1500ms), and clear the _probe_worker reference to avoid dangling threads. Also make a copy of the QImage created from the frame buffer (QImage(...).copy()) so the Qt image doesn't reference the underlying frame memory, preventing use-after-free crashes or visual corruption.
Camera dialog: tighten preview lifecycle and disabled-control handling. Introduces a preview-starting flag, commits pending edits before starting preview, consolidates loader/start/stop flow, and treats exposure/gain as 0 when their controls are disabled so they won't trigger unnecessary restarts. Also fixes various preview UI/loader state transitions and button states during async start/stop.

Tests: large refactor of test fixtures to provide deterministic fake backends and DLCLive doubles. Adds reusable test backend helpers (make_backend_class, temp_backend, register_fake_backend_session), a stable fake_backend_factory, and simplifies GUI autouse patches. Expands and reorganizes unit and end-to-end camera-config tests to cover preview start/stop, restart/no-restart semantics, scan cancellation, duplicate/max-camera guards, commit-on-select/OK behavior, crop validation, and backend capability handling. Updates gui conftest to use the new fake backend and DLCLive test doubles.
Stabilize GUI tests by making backend discovery and capabilities deterministic. Add _select_backend_for_active_cam helper to ensure combo/backend coherence during tests. Replace global fake/opencv mixing with a patch_detect_cameras fixture (staticmethod) and use a stable 'fake' backend in E2E fixtures. Inline CountingBackend in relevant tests and monkeypatch CameraFactory.create as staticmethod to avoid hardware access. In unit tests, patch backend_capabilities for predictable enable/disable state and switch assertions to FPS (supported) instead of gain. Misc: minor test cleanups, docstring tweaks, and staticmethod adjustments for slow scan/loader helpers.
@deruyter92 deruyter92 marked this pull request as ready for review February 13, 2026 13:40
@C-Achard C-Achard changed the base branch from cy/upgrade-aravis-backend to cy/pre-release-fixes-2.0 February 13, 2026 13:54
* Extract camera loaders; refactor preview UI state

Move camera worker and preview state logic into a new module (dlclivegui/gui/camera_loaders.py) and refactor the CameraConfigDialog to use it. Added DetectCamerasWorker, CameraProbeWorker, CameraLoadWorker, PreviewState enum and PreviewSession dataclass to centralize loader/backend/timer intent. Removed the embedded worker classes from camera_config_dialog.py and replaced multiple booleans/flags with a single PreviewSession, an epoch-based signal invalidation mechanism, coalesced preview restarts, and unified scan/preview UI syncing. Also adjusted loader signal handlers to be epoch-aware, simplified start/stop flows, and made small docstring/comment tweaks in basler_backend.py regarding fast_start.

* Refactor camera config into package

Move camera configuration code into a dedicated gui/camera_config package and extract UI construction into a new ui_blocks module. Renamed camera_config_dialog.py and camera_loaders.py to camera_config/camera_config_dialog.py and camera_config/loaders.py respectively, replaced the large _setup_ui implementation with setup_camera_config_dialog_ui(dlg) from ui_blocks.py, and added ui_blocks.py to build the dialog UI. Updated relative imports in __init__.py, main_window.py, and affected modules/tests to the new paths. This refactor improves modularity and keeps the dialog file focused on logic rather than bulky widget construction.

* Refactor camera config dialog & add identity utils

Move camera identity utilities into cameras.factory (apply_detected_identity, camera_identity_key) and add CameraSettings.check_diff for concise settings diffs. Major refactor of CameraConfigDialog: reorganize UI/state helpers, probe/preview lifecycle, auto-apply pending edits, improved scan/probe cancellation and dialog reject handling, duplicate-camera checks, add reorder/add/remove safety, and split preview helpers into a new preview module; also remove unused cv2 import. These changes centralize identity handling, improve preview & probe UX, and add safer settings application and logging. Tests updated to match the new contracts.

* Centralize camera dialog cleanup and fixes

Add a unified _on_close_cleanup and closeEvent to ensure preview/worker shutdown and UI reset on dialog close/cancel. Make cleanup idempotent with a _cleanup_done guard, shorten worker wait times to reduce UI freeze, and defensively reset scan UI widgets. Connect scan cancel button handler. Initialize _multi_camera_settings fallback, tighten type annotations, and note eventFilter UI assignments. Refactor form/apply flow to build a new CameraSettings model, compute diffs, replace the working camera entry and update the active list item (apply now returns a truthy value). Call _reconcile_fps_from_backend when loading settings, remove exc_info from a loader error log, and invoke cleanup before accepting the dialog. These changes improve robustness during shutdown and reduce UI hangs.

* Refactor preview helpers into controller

Move preview state and session datatypes into preview.py and refactor low-level image ops into MultiCameraController static methods. preview.py now delegates rotation, crop, resize and pixmap conversion to MultiCameraController (with proper type hints and QTimer usage). loaders.py removes duplicate PreviewState/PreviewSession definitions and cleans imports. camera_config_dialog uses getattr to safely read backend.actual_fps and updates imports to match the refactor. MultiCameraController gains apply_rotation/apply_crop/apply_resize/ensure_color_* and to_display_pixmap utilities (default: no upscale when resizing).

* Update tests to use PreviewState and dialog._preview

Adapt camera config GUI tests to the refactored preview API: import PreviewState and replace checks against legacy dialog attributes (_loader, _preview_active, _preview_backend, _preview_timer) with dialog._preview.loader, dialog._preview.state (using PreviewState.*), dialog._preview.backend, and dialog._preview.timer. Update waitUntil conditions and assertions across test_cam_dialog_e2e.py to reflect the new preview state machine and object structure.
@C-Achard C-Achard changed the title Upgrade Gentll backend Upgrade Gentl backend Feb 16, 2026
Improve stability of test_max_cameras_prevented by waiting for the scan to finish before injecting results. Removed the backend='fake' remark from the docstring and added qtbot.waitUntil(lambda: not d._is_scan_running(), timeout=1000) and d._on_scan_finished() around the simulated scan result to ensure the dialog is in a stable state before interacting with UI widgets.
Introduce a new color_dropdowns module to provide colormap and bbox-color QComboBox helpers (gradient swatches, matplotlib registry integration, and enum-based BGR swatches). Refactor layouts.make_two_field_row (renamed and enhanced) to support flexible key/value pairs, styling, and optional spacing, and add enable_combo_shrink_to_current to size combos to their current item. Integrate these into the main window: wire a Visualization group with colormap and bbox color controls, use the new helpers to populate and manage combo state, add _on_colormap_changed, and update bbox color handling. Also update ui_blocks to use make_two_field_row, tweak several UI labels/rows, comment out the PYLON emulation env var, and change several CameraConfigDialog log statements from INFO to DEBUG for less noisy logging.
Introduce a ShrinkCurrentWidePopupComboBox and ComboSizing to make combobox controls shrink to the current selection while the popup widens to fit the longest item. Add factory helpers (make_colormap_combo, make_bbox_color_combo) that create/populate colormap and bbox-color combos with sizing, icons, tooltips and safer Matplotlib handling. Rename/refactor colormap helpers (list_colormap_names, _safe_mpl_colormaps_registry), improve gradient icon creation, and preserve editable behavior if Matplotlib is unavailable. Update main_window to use the new factories, pass sizing and icon options, and adjust key widths for layout consistency.
Add graceful SIGINT handling for the Qt app by installing a signal handler that closes the main window and starting a small QTimer keepalive so Python can process signals while the event loop runs. The timer is stored on QApplication as _sig_timer and cleaned up on aboutToQuit. In the GUI, replace plain QComboBox instances with color_ui.ShrinkCurrentWidePopupComboBox for processor and camera controls, and layout them together using lyts.make_two_field_row to produce a compact, stable row and avoid shifting.
Import the logging module and emit an informational log when a keyboard interrupt triggers _request_quit inside _maybe_allow_keyboard_interrupt. This adds visibility for debugging application shutdowns without changing existing close behavior.
Replace the previous side-panel layout with a QDockWidget-based controls panel to allow docking/undocking and prevent UI shifting. Extract stats layout into _build_stats_layout and enable selectable stats text. Add sizing/shrink options and placeholder for processor and camera combo boxes and call update_shrink_width at key points so combo widths adapt. Add controls toggle to the View menu, set dock features/options, and give the dock a stable objectName for state saving. Also stop the display timer on shutdown and perform minor UI/layout cleanups and refactors (imports and button/preview layout adjustments).
Allow the left controls dock to be closed independently by adding QDockWidget.DockWidgetClosable to its features. Hide the docked title bar by applying a transparent stylesheet to the controls dock to improve visual integration. Also add a separator in the View menu before the Appearance submenu
Prevent the Controls dock from being closed by the user (keep it movable/floatable) and replace the previous toggleViewAction with an explicit, checkable "Show controls" QAction in the View menu. The new action is synchronized with the dock's visibility (action toggled -> dock visibility; dock visibilityChanged -> action checked). Also minor reordering/cleanup of Appearance menu setup and comments.
Improve camera config loading UX and improve Basler backend
…-backend"

This reverts commit 03af146, reversing
changes made to fe62907.
@C-Achard C-Achard merged commit 48741fb into cy/pre-release-fixes-2.0 Feb 16, 2026
@C-Achard C-Achard mentioned this pull request Feb 16, 2026
15 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

camera Related to cameras and camera backends enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants